#include "zlmalloc.h"

#include "list.h"

#include <ctype.h>
#if defined(__linux__)
#include <pthread.h>
#elif defined(_WIN32)
#include <windows.h>
#endif

#if ( defined _WIN32 )
static char *_strndup (const char *s, size_t n)
{
  char *result;
  size_t len = strlen (s);

  if (n < len)
    len = n;

  result = (char *) ZLMALLOC (len + 1);
  if (!result)
    return 0;

  result[len] = '\0';
  return (char *) memcpy (result, s, len);
}
#endif

#ifndef STRDUP
#if ( defined __linux ) || ( defined __unix )
#define STRDUP	strdup
#define STRNDUP	strndup
#elif ( defined _WIN32 )
#define STRDUP	_strdup
#define STRNDUP	_strndup
#endif
#endif

#ifndef TLS
#if ( defined __linux__ ) || ( defined __unix ) || ( defined _AIX )
#define TLS             __thread
#elif ( defined _WIN32 )
#define TLS             __declspec(thread)
#endif
#endif

#ifndef RWLOCK
#if defined(__linux__)
#define RWLOCK		pthread_rwlock_t
#elif defined(_WIN32)
#define RWLOCK		CRITICAL_SECTION
#endif
#endif

#ifndef RWLOCK_INIT
#if defined(__linux__)
#define RWLOCK_INIT(_rwlock_)		pthread_rwlock_init( _rwlock_ , NULL );
#elif defined(_WIN32)
#define RWLOCK_INIT(_rwlock_)		InitializeCriticalSection( _rwlock_ );
#endif
#endif

#ifndef RWLOCK_WLOCK
#if defined(__linux__)
#define RWLOCK_WLOCK(_rwlock_)		pthread_rwlock_wrlock( _rwlock_ );
#elif defined(_WIN32)
#define RWLOCK_WLOCK(_rwlock_)		EnterCriticalSection( _rwlock_ );
#endif
#endif

#ifndef RWLOCK_RLOCK
#if defined(__linux__)
#define RWLOCK_RLOCK(_rwlock_)		pthread_rwlock_rdlock( _rwlock_ );
#elif defined(_WIN32)
#define RWLOCK_RLOCK(_rwlock_)		EnterCriticalSection( _rwlock_ );
#endif
#endif

#ifndef RWLOCK_UNLOCK
#if defined(__linux__)
#define RWLOCK_UNLOCK(_rwlock_)		pthread_rwlock_unlock( _rwlock_ );
#elif defined(_WIN32)
#define RWLOCK_UNLOCK(_rwlock_)		LeaveCriticalSection( _rwlock_ );
#endif
#endif

#ifndef RWLOCK_CLEAN
#if defined(__linux__)
#define RWLOCK_CLEAN(_rwlock_)		pthread_rwlock_destroy( _rwlock_ );
#elif defined(_WIN32)
#define RWLOCK_CLEAN(_rwlock_)		DeleteCriticalSection( _rwlock_ );
#endif
#endif

#define __DEBUG		0

#if __DEBUG
#define __TRACE(...)		printf( __VA_ARGS__ ); fflush(stdout);
#else
#define __TRACE(...)
#endif

unsigned char		__zlmalloc_use_stdlib = 0 ;

struct ZlMemBlocks
{
	struct list_head	mem_list ;
} ;
TLS RWLOCK		__zlmalloc_rwlock ;
TLS struct ZlMemBlocks	__zlmalloc_memblocks = { {NULL,NULL} } ;
TLS size_t		__zlmalloc_using_blockcount = 0 ;
TLS size_t		__zlmalloc_using_memsize = 0 ;

struct ZlMemBlock
{
	char			*action ;
	size_t			size ;
	char			*file ;
	size_t			line ;
	
	RWLOCK			*zlmalloc_rwlock ;
	struct ZlMemBlocks	*zlmalloc_memblocks ;
	size_t			*zlmalloc_using_blockcount ;
	size_t			*zlmalloc_using_memsize ;
	
	struct list_head	mem_list_node ;
} ;

#define INIT_MEMBLOCKS_LIST \
	if( __zlmalloc_memblocks.mem_list.prev == NULL || __zlmalloc_memblocks.mem_list.next == NULL ) \
	{ \
		RWLOCK_INIT( & __zlmalloc_rwlock ) \
		INIT_LIST_HEAD( & (__zlmalloc_memblocks.mem_list) ); \
		__zlmalloc_using_memsize = 0 ; \
		__zlmalloc_using_blockcount = 0 ; \
	} \

char *ConvPrintChars( char *from , size_t from_size )
{
	static char	to[ 24 ] = "" ;
	size_t		to_idx ;
	char		*p_to = to ;
	char		*p_from = from ;
	
	for( to_idx = 0 ; to_idx < sizeof(to)-1 ; to_idx++ , p_from++ , p_to++ )
	{
		if( to_idx >= from_size-1 )
			break;
		
		if( isprint(*p_from) )
			(*p_to) = (*p_from) ;
		else
			(*p_to) = ' ' ;
	}
	(*p_to) = '\0' ;
	
	return to;
}

void *_zlmalloc( char *action , char *file , size_t line , size_t size )
{
	struct ZlMemBlock	*memblock ;
	
	if( __zlmalloc_use_stdlib )
		return malloc( size );
	
	INIT_MEMBLOCKS_LIST
	
	memblock = (struct ZlMemBlock *)malloc( sizeof(struct ZlMemBlock) + size ) ;
	if( memblock == NULL )
		return NULL;
	
	memblock->action = action ;
	memblock->size = size ;
	memblock->file = file ;
	memblock->line = line ;
	memblock->zlmalloc_rwlock = & __zlmalloc_rwlock ;
	memblock->zlmalloc_memblocks = & __zlmalloc_memblocks ;
	memblock->zlmalloc_using_blockcount = & __zlmalloc_using_blockcount ;
	memblock->zlmalloc_using_memsize = & __zlmalloc_using_memsize ;
	
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_wrlock ...\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	RWLOCK_WLOCK( & __zlmalloc_rwlock );
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_wrlock return\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	{
		__TRACE( "ZLMALLOC - %010zu - %s:%d:%s [%zu] - action[%s]size[%zu]source[%s:%zu] [%p] return[%p] - [%p]blockcount++ [%zu]->[%zu] , [%p]memsize+=[%zu] [%zu]->[%zu]\n"
			, pthread_self() , __FILE__,__LINE__,__FUNCTION__ , size
			, memblock->action , memblock->size , memblock->file,memblock->line , (char*)memblock+sizeof(struct ZlMemBlock)
			, (char*)memblock+sizeof(struct ZlMemBlock)
			, & __zlmalloc_using_blockcount , __zlmalloc_using_blockcount , __zlmalloc_using_blockcount+1
			, & __zlmalloc_using_memsize , size , __zlmalloc_using_memsize , __zlmalloc_using_memsize+size ); fflush(stdout);
		
		list_add_tail( & (memblock->mem_list_node) , & (__zlmalloc_memblocks.mem_list) );
		__zlmalloc_using_memsize += size ;
		__zlmalloc_using_blockcount++;
	}
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_unlock ...\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	RWLOCK_UNLOCK( & __zlmalloc_rwlock );
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_unlock return\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	
	return (char*)memblock+sizeof(struct ZlMemBlock);
}

void _zlfree( char *action , char *file , size_t line , void *ptr )
{
	struct ZlMemBlock	*memblock ;
	
	if( __zlmalloc_use_stdlib )
	{
		free( ptr );
		return;
	}
	
	INIT_MEMBLOCKS_LIST
	
	if( ptr == NULL )
		return;
	
	memblock = (struct ZlMemBlock *)( (char*)ptr - sizeof(struct ZlMemBlock) ) ;
	
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_wrlock ...\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	RWLOCK_WLOCK( memblock->zlmalloc_rwlock );
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_wrlock return\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	{
		__TRACE( "ZLMALLOC - %010zu - %s:%d:%s [%p] - action[%s]size[%zu]source[%s:%zu] [%p] [%s] return - [%p]blockcount-- [%zu]->[%zu] , [%p]memsize-=[%zu] [%zu]->[%zu]\n"
			, pthread_self() , __FILE__,__LINE__,__FUNCTION__ , ptr
			, memblock->action , memblock->size , memblock->file,memblock->line , ptr , ConvPrintChars(ptr,memblock->size)
			, memblock->zlmalloc_using_blockcount , *(memblock->zlmalloc_using_blockcount) , *(memblock->zlmalloc_using_blockcount)-1
			, memblock->zlmalloc_using_memsize , memblock->size , *(memblock->zlmalloc_using_memsize) , *(memblock->zlmalloc_using_memsize)-memblock->size ); fflush(stdout);
		
		list_del( & (memblock->mem_list_node) );
		(*(memblock->zlmalloc_using_blockcount))--;
		*(memblock->zlmalloc_using_memsize) -= memblock->size ;
	}
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_unlock ...\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	RWLOCK_UNLOCK( memblock->zlmalloc_rwlock );
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_unlock return\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	
	free( memblock );
	
	return;
}

void *_zlcalloc( char *action , char *file , size_t line , size_t nmemb , size_t size )
{
	if( __zlmalloc_use_stdlib )
		return calloc( nmemb , size );
	
	INIT_MEMBLOCKS_LIST
	
	return _zlmalloc( action , file , line , nmemb*size );
}

void *_zlrealloc( char *action , char *file , size_t line , void *ptr , size_t size )
{
	size_t			diff_size ;
	struct ZlMemBlock	*memblock = NULL ;
	struct ZlMemBlock	*new_memblock = NULL ;
	
	if( __zlmalloc_use_stdlib )
		return realloc( ptr , size );
	
	INIT_MEMBLOCKS_LIST
	
	if( ptr == NULL )
		return _zlmalloc( action , file , line , size );
	
	memblock = (struct ZlMemBlock*)( (char*)ptr - sizeof(struct ZlMemBlock) ) ;
	diff_size = size - memblock->size ;
	
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_wrlock ...\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	RWLOCK_WLOCK( memblock->zlmalloc_rwlock );
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_wrlock return\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	{
		new_memblock = (struct ZlMemBlock *)realloc( memblock , sizeof(struct ZlMemBlock) + size ) ;
		if( new_memblock == NULL )
		{
			RWLOCK_UNLOCK( memblock->zlmalloc_rwlock );
			return NULL;
		}
		memblock = new_memblock ;
		
		memblock->action = action ;
		memblock->size = size ;
		memblock->file = file ;
		memblock->line = line ;
		
		__TRACE( "ZLMALLOC - %010zu - %s:%d:%s [%p][%zu] - action[%s]size[%zu]source[%s:%zu] [%p] return[%p] - [%p]blockcount[%zu] , [%p]memsize-=[%zu] [%zu]->[%zu]\n"
			, pthread_self() , __FILE__,__LINE__,__FUNCTION__ , ptr , size
			, memblock->action , memblock->size , memblock->file,memblock->line , (char*)ptr-sizeof(struct ZlMemBlock) , (char*)memblock+sizeof(struct ZlMemBlock)
			, memblock->zlmalloc_using_blockcount , *(memblock->zlmalloc_using_blockcount)
			, memblock->zlmalloc_using_memsize , diff_size , *(memblock->zlmalloc_using_memsize) , *(memblock->zlmalloc_using_memsize)+diff_size ); fflush(stdout);
		
		list_del( & (memblock->mem_list_node) );
		list_add_tail( & (memblock->mem_list_node) , & (memblock->zlmalloc_memblocks->mem_list) );
		*(memblock->zlmalloc_using_memsize) += diff_size ;
	}
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_unlock ...\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	RWLOCK_UNLOCK( memblock->zlmalloc_rwlock );
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_unlock return\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	
	return (char*)memblock+sizeof(struct ZlMemBlock);
}

char *_zlstrdup( char *action , char *file , size_t line , char *s )
{
	struct ZlMemBlock	*memblock ;
	size_t			str_size ;
	char			*p ;
	
	if( __zlmalloc_use_stdlib )
		return STRDUP( s );
	
	INIT_MEMBLOCKS_LIST
	
	if( s == NULL )
		return NULL;
	
	str_size = strlen(s) + 1 ;
	memblock = (struct ZlMemBlock *)malloc( sizeof(struct ZlMemBlock) + str_size ) ;
	if( memblock == NULL )
		return NULL;
	
	p = (char*)memblock + sizeof(struct ZlMemBlock) ;
	memmove( p , s , str_size );
	
	memblock->action = action ;
	memblock->size = str_size ;
	memblock->file = file ;
	memblock->line = line ;
	memblock->zlmalloc_rwlock = & __zlmalloc_rwlock ;
	memblock->zlmalloc_memblocks = & __zlmalloc_memblocks ;
	memblock->zlmalloc_using_blockcount = & __zlmalloc_using_blockcount ;
	memblock->zlmalloc_using_memsize = & __zlmalloc_using_memsize ;
	
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_wrlock ...\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	RWLOCK_WLOCK( & __zlmalloc_rwlock );
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_wrlock return\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	{
		__TRACE( "ZLMALLOC - %010zu - %s:%d:%s [%.30s%s] - action[%s]size[%zu]source[%s:%zu] [%p] return[%p] - [%p]blockcount++ [%zu]->[%zu] , [%p]memsize+=[%zu] [%zu]->[%zu]\n"
			, pthread_self() , __FILE__,__LINE__,__FUNCTION__ , s,str_size>=30?"...":""
			, memblock->action , memblock->size , memblock->file,memblock->line , p
			, p
			, & __zlmalloc_using_blockcount , __zlmalloc_using_blockcount , __zlmalloc_using_blockcount+1
			, & __zlmalloc_using_memsize , str_size , __zlmalloc_using_memsize , __zlmalloc_using_memsize+str_size ); fflush(stdout);
		
		list_add_tail( & (memblock->mem_list_node) , & (__zlmalloc_memblocks.mem_list) );
		__zlmalloc_using_blockcount++;
		__zlmalloc_using_memsize += str_size ;
	}
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_unlock ...\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	RWLOCK_UNLOCK( & __zlmalloc_rwlock );
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_unlock return\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	
	return p;
}

char *_zlstrndup( char *action , char *file , size_t line , char *s , size_t n )
{
	struct ZlMemBlock	*memblock ;
	size_t			str_size ;
	char			*p ;
	
	if( __zlmalloc_use_stdlib )
		return STRNDUP( s , n );
	
	INIT_MEMBLOCKS_LIST
	
	if( s == NULL )
		return NULL;
	
	str_size = n + 1 ;
	memblock = (struct ZlMemBlock *)malloc( sizeof(struct ZlMemBlock) + str_size ) ;
	if( memblock == NULL )
		return NULL;
	
	p = (char*)memblock + sizeof(struct ZlMemBlock) ;
	memmove( p , s , n );
	p[n] = '\0' ;
	
	memblock->action = action ;
	memblock->size = str_size ;
	memblock->file = file ;
	memblock->line = line ;
	memblock->zlmalloc_rwlock = & __zlmalloc_rwlock ;
	memblock->zlmalloc_memblocks = & __zlmalloc_memblocks ;
	memblock->zlmalloc_using_blockcount = & __zlmalloc_using_blockcount ;
	memblock->zlmalloc_using_memsize = & __zlmalloc_using_memsize ;
	
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_wrlock ...\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	RWLOCK_WLOCK( & __zlmalloc_rwlock );
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_wrlock return\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	{
		__TRACE( "ZLMALLOC - %010zu - %s:%d:%s [%.*s%s] - action[%s]size[%zu]source[%s:%zu] [%p] return[%p] - [%p]blockcount++ [%zu]->[%zu] , [%p]memsize+=[%zu] [%zu]->[%zu]\n"
			, pthread_self() , __FILE__,__LINE__,__FUNCTION__ , (int)(n<30?n:30),s,n>=30?"...":""
			, memblock->action , memblock->size , memblock->file,memblock->line , p
			, p
			, & __zlmalloc_using_blockcount , __zlmalloc_using_blockcount , __zlmalloc_using_blockcount+1
			, & __zlmalloc_using_memsize , str_size , __zlmalloc_using_memsize , __zlmalloc_using_memsize+str_size ); fflush(stdout);
		
		list_add_tail( & (memblock->mem_list_node) , & (__zlmalloc_memblocks.mem_list) );
		__zlmalloc_using_blockcount++;
		__zlmalloc_using_memsize += str_size ;
	}
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_unlock ...\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	RWLOCK_UNLOCK( & __zlmalloc_rwlock );
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_unlock return\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	
	return p;
}

size_t ZlGetUsingMemory()
{
	INIT_MEMBLOCKS_LIST
	
	return __zlmalloc_using_memsize;
}

size_t ZlGetUsingBlocks()
{
	INIT_MEMBLOCKS_LIST
	
	return __zlmalloc_using_blockcount;
}

size_t ZlGetUsingTotalMemory()
{
	INIT_MEMBLOCKS_LIST
	
	return __zlmalloc_using_memsize+__zlmalloc_using_blockcount*sizeof(struct ZlMemBlock);
}

struct ZlMemBlocks *ZlGetMemBlocks()
{
	INIT_MEMBLOCKS_LIST
	
	return &__zlmalloc_memblocks;
}

struct ZlMemBlock *ZlTravelMemoryBlock( struct ZlMemBlocks *memblocks , struct ZlMemBlock *memblock )
{
	INIT_MEMBLOCKS_LIST
	
	if( memblock == NULL )
		return list_first_entry_or_null( & (memblocks->mem_list) , struct ZlMemBlock , mem_list_node );
	else
		return list_next_entry_or_null( memblock , & (memblocks->mem_list) , struct ZlMemBlock , mem_list_node );
}

char *ZlGetMemblockBase( struct ZlMemBlock *memblock )
{
	return (char*)memblock+sizeof(struct ZlMemBlock);
}

char *ZlGetMemblockAction( struct ZlMemBlock *memblock )
{
	return memblock->action;
}

size_t ZlGetMemblockSize( struct ZlMemBlock *memblock )
{
	return memblock->size;
}

char *ZlGetMemblockSourceFile( struct ZlMemBlock *memblock )
{
	return memblock->file;
}

size_t ZlGetMemblockSourceLine( struct ZlMemBlock *memblock )
{
	return memblock->line;
}

int ZlWriteLockMemblocks()
{
	int		nret = 0 ;
	
	INIT_MEMBLOCKS_LIST
	
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_wrlock ...\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	RWLOCK_WLOCK( & __zlmalloc_rwlock );
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_wrlock return[%d]\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ , nret )
	
	return nret;
}

int ZlReadLockMemblocks()
{
	int		nret = 0 ;
	
	INIT_MEMBLOCKS_LIST
	
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_rdlock ...\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	RWLOCK_RLOCK( & __zlmalloc_rwlock );
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_rdlock return[%d]\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ , nret )
	
	return nret;
}

int ZlUnlockMemblocks()
{
	int		nret = 0 ;
	
	INIT_MEMBLOCKS_LIST
	
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_unlock ...\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ )
	RWLOCK_UNLOCK( & __zlmalloc_rwlock );
	__TRACE( "ZLMALLOC - %010zu - %s:%d:%s - pthread_rwlock_unlock return[%d]\n" , pthread_self() , __FILE__,__LINE__,__FUNCTION__ , nret )
	
	return nret;
}

unsigned char ZlUseStdlib( unsigned char enable )
{
	__zlmalloc_use_stdlib = enable ;
	
	return __zlmalloc_use_stdlib;
}

